package com.aishua.fire.config.init;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.aishua.fire.common.MD5Util;
import com.aishua.fire.common.SpringContextUtil;
import com.aishua.fire.common.anno.ModuleService;
import com.aishua.fire.common.anno.PermMapping;
import com.aishua.fire.common.constant.DictConstants;
import com.aishua.fire.common.constant.PermInitConstants;
import com.aishua.fire.common.constant.SoftdelConstants;
import com.aishua.fire.jpa.AccountAccountteamDao;
import com.aishua.fire.jpa.AccountDao;
import com.aishua.fire.jpa.AccountteamDao;
import com.aishua.fire.jpa.AccountteamPermteamDao;
import com.aishua.fire.jpa.OneLevelMenuDao;
import com.aishua.fire.jpa.PermDao;
import com.aishua.fire.jpa.PermPermteamDao;
import com.aishua.fire.jpa.PermteamDao;
import com.aishua.fire.jpa.TwoLevelMenuDao;
import com.aishua.fire.jpa.entity.Account;
import com.aishua.fire.jpa.entity.AccountAccountteam;
import com.aishua.fire.jpa.entity.AccountAccountteamId;
import com.aishua.fire.jpa.entity.Accountteam;
import com.aishua.fire.jpa.entity.AccountteamPermteam;
import com.aishua.fire.jpa.entity.AccountteamPermteamId;
import com.aishua.fire.jpa.entity.OneLevelMenu;
import com.aishua.fire.jpa.entity.Perm;
import com.aishua.fire.jpa.entity.PermPermteam;
import com.aishua.fire.jpa.entity.PermPermteamId;
import com.aishua.fire.jpa.entity.Permteam;
import com.aishua.fire.jpa.entity.TwoLevelMenu;
import com.aishua.fire.service.LoginService;

import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class Init implements ApplicationRunner {

	@Autowired
	private AccountDao accountDao;
	@Autowired
	private AccountAccountteamDao accountAccountteamDao;
	@Autowired
	private AccountteamDao accountteamDao;
	@Autowired
	private AccountteamPermteamDao accountteamPermteamDao;
	@Autowired
	private PermteamDao permteamDao;
	@Autowired
	private PermPermteamDao permPermteamDao;
	@Autowired
	private PermDao permDao;
	@Autowired
	private LoginService loginService;
	
	@Autowired
	private OneLevelMenuDao oneLevelMenuDao;
	@Autowired
	private TwoLevelMenuDao twoLevelMenuDao;
	//url权限列表集合
	public static ConcurrentHashMap<PermInfo, Collection<ConfigAttribute>> urlMap;
	
	
	@Override
	public void run(ApplicationArguments applicationArgs) throws Exception {
		//持久化url+账户组集列表
        initUrlMap(true);
	}
	
	
	public void urlPermInit() {
		Account adminOld = accountDao.findOne(1L);
		if(adminOld == null) {
	    	//添加默认的管理员账户
	    	Account admin = new Account("admin", MD5Util.md5("admin"), "管理员", "13000000000", new Date(), "0", "123@test.com");
	    	admin.setLoginId(Long.parseLong("-1"));
	    	admin.setAccountStatus(DictConstants.ACCOUNT_YES);
	    	admin.setStatus(SoftdelConstants.DEL_N);
	    	admin.setLastLoginTime(new Date());
	    	accountDao.save(admin);
	    	loginService.insertSession(Long.parseLong("-1"));
		}
		
		
    	//管理员
    	AccountAccountteam accountAccountteam = new AccountAccountteam();
		accountAccountteam.setAccountAccountteamId(new AccountAccountteamId(1,1));
		accountAccountteamDao.save(accountAccountteam);
		
    	//账户组(管理员)
    	Accountteam accountteam = new Accountteam(DictConstants.ADMIN_ACCTEAM_NAME,DictConstants.ADMIN_ACCTEAM_DESC,new Date());
    	accountteam.setAccountteamId(1L);
    	accountteamDao.save(accountteam);
    	
		//账户组_权限组(管理员)
		AccountteamPermteam accountteamPermteam = new AccountteamPermteam();
		accountteamPermteam.setAccountteamPermteamId(new AccountteamPermteamId(1,1));
		accountteamPermteam.setAuthType(DictConstants.PERM_AUTHTYPE_ALLOW);
		accountteamPermteam.setAuthAction(DictConstants.PERM_AUTHACTION_ALWAYS);
		accountteamPermteam.setCreateDate(new Date());
		accountteamPermteamDao.save(accountteamPermteam);
		
		//权限组(管理员)
		Permteam permteam = new Permteam("管理员超级权限","超级权限",new Date(),SoftdelConstants.DEL_N);
		permteam.setPermteamId(1L);
		permteamDao.save(permteam);
		
		//子权限_权限组
        List<PermPermteam> list = new ArrayList<PermPermteam>();

		RequestMappingHandlerMapping rmhp = SpringContextUtil.getBeanByType(RequestMappingHandlerMapping.class); 
		Map<RequestMappingInfo, HandlerMethod> map = rmhp.getHandlerMethods();  
        for (Iterator<RequestMappingInfo> iterator = map.keySet().iterator(); iterator    
                .hasNext();) {    
            RequestMappingInfo info = iterator.next();  
            log.debug("{}",info.getMethodsCondition());  //[POST]
            log.debug("{}",info.getPatternsCondition());  //[/accountAteam]
            HandlerMethod method = map.get(info); 
            log.debug("控制层类:{}",method.getBeanType());
            ApiOperation apiOperation = method.getMethod().getAnnotation(ApiOperation.class);
            ModuleService moduleService = method.getMethod().getAnnotation(ModuleService.class);
            PermMapping permMapping = method.getMethod().getAnnotation(PermMapping.class);
            	if(method.getBeanType().toString().substring(0, 9).equals(PermInitConstants.Bean_Type)) {
            		
            		TwoLevelMenu twoLevelMenu = null;
            		
            		if(moduleService != null) {
			        	  //权限表
			        	  OneLevelMenu oneLevelMenu = oneLevelMenuDao.findByOneLevelMenu(moduleService.totalModule());
			        	  if(oneLevelMenu == null) {
			        		  oneLevelMenu = new OneLevelMenu();
			        		  oneLevelMenu.setCreateDate(new Date());
			        		  oneLevelMenu.setOneLevelMenu(moduleService.totalModule());
			        		  oneLevelMenu = oneLevelMenuDao.saveAndFlush(oneLevelMenu);
			        	  }
			        	  
			        	  twoLevelMenu = twoLevelMenuDao.findByTwoLevelMenu(moduleService.moduleName());
		            	  if(twoLevelMenu == null) {
		            		  twoLevelMenu = new TwoLevelMenu();
		            		  twoLevelMenu.setCreateDate(new Date());
		            		  twoLevelMenu.setTwoLevelMenu(moduleService.moduleName());
		            		  twoLevelMenu.setOneLevelId(String.valueOf(oneLevelMenu.getId()));
		            		  twoLevelMenu = twoLevelMenuDao.save(twoLevelMenu);
		            	  }
            		
            		}
		             
            		String requestName = info.getPatternsCondition().toString().substring(1, info.getPatternsCondition().toString().length()-1);
	            	String requestMethod = info.getMethodsCondition().toString().substring(1, info.getMethodsCondition().toString().length()-1);
	            	  
            		Perm perm =permDao.findByNameAndMethod(requestName,requestMethod);
	            	  if(perm == null) {
	            		  perm = new Perm();
	            		  perm.setRequestMethod(requestMethod);
	            		  perm.setPermName(requestName);
	            		  perm.setStatus(SoftdelConstants.DEL_N);
	            		  perm.setPermDesc(apiOperation.value());
	            		  perm.setCreateDate(new Date());
				          perm.setType(DictConstants.URL_TYPE);
				          //添加类名
				          perm.setClassName(method.getBeanType().toString().replaceAll("class ", ""));
				          if(twoLevelMenu != null) {
				        	  perm.setTotalModuleName(String.valueOf(twoLevelMenu.getId()));
				          }
				          
				          //添加是否需要白名单权限
				          if(permMapping !=null && permMapping.onlyLogin()) {
				        	  perm.setIsBlank(DictConstants.ISBLANK_PERM_YES);
				          }else {
				        	  perm.setIsBlank(DictConstants.ISBLANK_PERM_NO);
				          }
				          
				          //添加非验证白名单接口
				          if(permMapping !=null && permMapping.noValidate()) {
				        	  perm.setNoValidate(DictConstants.NO_VALIDATE_YES);
				          }else {
				        	  perm.setNoValidate(DictConstants.NO_VALIDATE_NO);
				          }
				          
				      }else {
				    	  //否则更新
				    	  perm.setPermDesc(apiOperation.value());
				    	  perm.setCreateDate(new Date());
				    	  perm.setStatus(SoftdelConstants.DEL_N);
				    	  perm.setType(DictConstants.URL_TYPE);
				          //添加类名
				          perm.setClassName(method.getBeanType().toString().replaceAll("class ", ""));
				          if(twoLevelMenu != null) {
				        	  perm.setTotalModuleName(String.valueOf(twoLevelMenu.getId()));
				          }
				          
				          //添加是否需要白名单权限
				          if(permMapping !=null && permMapping.onlyLogin()) {
				        	  perm.setIsBlank(DictConstants.ISBLANK_PERM_YES);
				          }else {
				        	  perm.setIsBlank(DictConstants.ISBLANK_PERM_NO);
				          }
				          
				        //添加非验证白名单接口
				          if(permMapping !=null && permMapping.noValidate()) {
				        	  perm.setNoValidate(DictConstants.NO_VALIDATE_YES);
				          }else {
				        	  perm.setNoValidate(DictConstants.NO_VALIDATE_NO);
				          }
				      }
				          //perms.add(perm);
				      perm = permDao.saveAndFlush(perm);
	              
				      //管理员
		              PermPermteam permPermteam = permPermteamDao.findByUnionId(1L,perm.getPermId());
		              if(permPermteam == null) {
		            	  permPermteam = new PermPermteam();
		            	  permPermteam.setPermpermteamId(new PermPermteamId(1,perm.getPermId()));
		            	  list.add(permPermteam);
		              }
                }
        }
		permPermteamDao.save(list);
			   
		
	}

	/**
	 * 查找权限对应角色列表
	 */
	public void initUrlMap(boolean isLoged) {
		synchronized (Init.class) {
			if(urlMap == null) {
				urlMap = new ConcurrentHashMap<PermInfo, Collection<ConfigAttribute>>();
			}else {
				urlMap.clear();
			}
			//查找所有的权限
			List<Perm> perms = permDao.findAllMapByType(DictConstants.URL_TYPE);
			if(isLoged) {
				log.info("loadResourceMatchAuthority的值是:{}",perms);
			}
			for(Perm perm : perms) {
				//账户组列表
				List<Accountteam> ateams = accountteamDao.findAteamsWithUrlByPermId(perm.getPermId(),DictConstants.URL_TYPE,DictConstants.PERM_AUTHTYPE_ALLOW);
				Collection<ConfigAttribute> list = new CopyOnWriteArrayList<ConfigAttribute>();
				 for (Accountteam ateam : ateams) {
		                ConfigAttribute config = new SecurityConfig(String.valueOf(ateam.getAccountteamId()));
		                list.add(config);
		         }
				 if(isLoged) {
					 log.info("数据库查询出来的[权限名+方法]:{}+{}",perm.getPermName(),perm.getRequestMethod());
				 }
				String regex = "\\{[^\\}]+\\}";
				String uriRegex = perm.getPermName().replaceAll(regex, "[^\\\\}]+");
				PermInfo permInfo = new PermInfo();
				permInfo.setPermName(uriRegex);
				permInfo.setCreateDate(perm.getCreateDate());
				permInfo.setModuleName(perm.getModuleName());
				permInfo.setPermDesc(perm.getPermDesc());
				permInfo.setPermId(perm.getPermId());
				permInfo.setRequestMethod(perm.getRequestMethod());
				permInfo.setStatus(perm.getStatus());
				permInfo.setTotalModuleName(perm.getTotalModuleName());
				permInfo.setType(perm.getType());
				permInfo.setIsBlank(perm.getIsBlank());
				permInfo.setNoValidate(perm.getNoValidate());
				//log.info("请求接口的uri值是{},方法名是:{}",uriRegex,method);
				urlMap.put(permInfo, list);
			}
			log.info("{}",urlMap);
		}
	}
	
	/**
	 * 添加角色,权限缓存刷新
	 */
	public void addAccountteam(long accountteamId) {
		if(urlMap == null) {
			urlMap = new ConcurrentHashMap<PermInfo, Collection<ConfigAttribute>>();
			log.info("urlMap为空(添加角色)");
			return;
		}
		
		//通过角色id查出对应的拥有权限列表
		List<Perm> perms = permDao.getPermsByAccountteamId(accountteamId,DictConstants.URL_TYPE,DictConstants.PERM_AUTHTYPE_ALLOW);
		
		for(Map.Entry<PermInfo, Collection<ConfigAttribute>> entry : urlMap.entrySet()) {
			PermInfo perm = entry.getKey();
			long permId = perm.getPermId();
			
			Collection<ConfigAttribute> accountteams = entry.getValue();
			
			for(Perm p:perms) {
				if(permId == p.getPermId() && !hasAccountteam(accountteams,accountteamId)) {
					ConfigAttribute config = new SecurityConfig(String.valueOf(accountteamId));
					accountteams.add(config);
				}
			}
		}
	}
	
	/**
	 * 删除角色,权限缓存刷新(删除时,先清缓存,然后同步数据库)
	 */
	public void delAccountteam(long accountteamId) {
		synchronized (Init.class) {
			if(urlMap == null) {
				urlMap = new ConcurrentHashMap<PermInfo, Collection<ConfigAttribute>>();
				log.info("urlMap为空(删除角色)");
				return;
			}
			
			//通过角色id查出对应的拥有权限列表
			List<Perm> perms = permDao.getPermsByAccountteamId(accountteamId,DictConstants.URL_TYPE,DictConstants.PERM_AUTHTYPE_ALLOW);
			
			for(Map.Entry<PermInfo, Collection<ConfigAttribute>> entry : urlMap.entrySet()) {
				PermInfo perm = entry.getKey();
				long permId = perm.getPermId();
				
				Collection<ConfigAttribute> accountteams = entry.getValue();
				
				for(Perm p:perms) {
					if(permId == p.getPermId()) {
						Iterator<ConfigAttribute> iterator = accountteams.iterator();
				        while(iterator.hasNext()) {
				            ConfigAttribute configAttribute = iterator.next();
				            //访问所请求资源所需要的权限
				            String hasRole = configAttribute.getAttribute();
				            if(hasRole.equals(String.valueOf(accountteamId))) {
				            	accountteams.remove(configAttribute);
				            }
				        }
					}
				}
			}
		}
	}
	
	public void addPermteam(long permteamId) {
		//通过权限组id获取所有的角色id
		List<AccountteamPermteam> relateAccountteamPermteams = accountteamPermteamDao.getAccountteamPermteamByPermteamId(permteamId,DictConstants.PERM_AUTHTYPE_ALLOW);
		for(AccountteamPermteam ateampteam:relateAccountteamPermteams) {
			addAccountteam(ateampteam.getAccountteamPermteamId().getAccountteamId());
		}
	}
	
	/**
	 * (删除时,先清缓存,然后同步数据库)
	 * @param permteamId
	 */
	public void delPermteam(long permteamId) {
		List<AccountteamPermteam> relateAccountteamPermteams = accountteamPermteamDao.getAccountteamPermteamByPermteamId(permteamId,DictConstants.PERM_AUTHTYPE_ALLOW);;
		for(AccountteamPermteam ateampteam:relateAccountteamPermteams) {
			delAccountteam(ateampteam.getAccountteamPermteamId().getAccountteamId());
		}
	}
	
	/**
	 * 当前角色id是否存在
	 * @param accountteams
	 * @param accountteamId
	 * @return
	 */
	private boolean hasAccountteam(Collection<ConfigAttribute> accountteams,long accountteamId){
		Iterator<ConfigAttribute> iterator = accountteams.iterator();
        while(iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //访问所请求资源所需要的权限
            String hasRole = configAttribute.getAttribute();
            if(hasRole.equals(String.valueOf(accountteamId))) {
                return true;
            }
        }
        return false;
	}
	
	

	
	/**
	 * 获取url 
	 * @return
	 */
	public static ConcurrentHashMap<PermInfo, Collection<ConfigAttribute>> getUrlMap() {
		return urlMap;
	}

	/**
	 * 是否为验证接口
	 * @param request
	 * @return
	 */
	public static boolean isNoValidateInterface(HttpServletRequest request) {
		String method = request.getMethod();
		//如果是options请求,默认返回true
		if("options".equalsIgnoreCase(method)) {
			return true;
		}
		
		String uri = request.getRequestURI();
		
		ConcurrentHashMap<PermInfo, Collection<ConfigAttribute>> resourceMap = Init.getUrlMap();
		for(Map.Entry<PermInfo, Collection<ConfigAttribute>> entry : resourceMap.entrySet()) {
        	PermInfo perm = entry.getKey();
        	if(perm.getRequestMethod().equalsIgnoreCase(method)) {
        		String regex = perm.getPermName();
        		if(Pattern.matches(regex, uri) && DictConstants.NO_VALIDATE_YES.equals(perm.getNoValidate())) {
        				return true;
        		}
        	}
		}
        return false;
	}
	
}
